﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using Ionic.Zip;

namespace NewerthUpdater
{
    public partial class Main : Form
    {
        Thread thrMain;
        Dictionary<string, KeyValuePair<string, string>> updatedfiles = new Dictionary<string,KeyValuePair<string,string>>();
        bool inProgress = false;
        Version version = new Version();

        public Main()
        {
            InitializeComponent();
        }

        private void Main_Shown(object sender, EventArgs e)
        {
            lblStatus.Text = "Closing NewerthBot...";

            foreach (Process process in Process.GetProcesses())
            {
                if (process.ProcessName == "NewerthBot.exe")
                {
                    process.Close();
                    while (!process.HasExited) { };
                    break;
                }
            }

            lblStatus.Text = "Checking for updates...";

            thrMain = new Thread(delegate() { CheckForUpdates(); });
            thrMain.Start();
            while (thrMain.ThreadState != System.Threading.ThreadState.Stopped) { Application.DoEvents(); };

            if (updatedfiles.Count > 0)
            {
                inProgress = true;
                lblStatus.Text = "Downloading update....";

                thrMain = new Thread(delegate() { DownloadUpdate(); });
                thrMain.Start();
                while (thrMain.ThreadState != System.Threading.ThreadState.Stopped) { Application.DoEvents(); };

                lblStatus.Text = "Extracting update...";

                thrMain = new Thread(delegate() { ExtractUpdate(); });
                thrMain.Start();
                while (thrMain.ThreadState != System.Threading.ThreadState.Stopped) { Application.DoEvents(); };

                lblStatus.Text = "Updating manifest...";

                thrMain = new Thread(delegate() { UpdateManifest(); });
                thrMain.Start();
                while (thrMain.ThreadState != System.Threading.ThreadState.Stopped) { Application.DoEvents(); };
            }

            Process.Start(Application.StartupPath + "/NewerthBot.exe");
            Application.Exit();
        }

        private void CheckForUpdates()
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlNodeList remote_manifest;
            XmlNodeList local_manifest;

            xmlDoc.Load(@"http://dl.dropbox.com/u/2683550/Heroes%20of%20Newerth/NewerthBot/manifest.xml");
            remote_manifest = xmlDoc.SelectNodes("/manifest/file");
            xmlDoc = null;

            xmlDoc = new XmlDocument();
            xmlDoc.Load(Application.StartupPath + "/manifest.xml");
            local_manifest = xmlDoc.SelectNodes("/manifest/file");

            foreach (XmlNode file in remote_manifest)
            {
                bool found = false;

                foreach (XmlNode localfile in local_manifest)
                {
                    if (file.Attributes["path"].Value == localfile.Attributes["path"].Value)
                    {
                        found = true;

                        if (new Version(file.Attributes["version"].Value) > new Version(localfile.Attributes["version"].Value))
                        {
                            if (version.ToString() == "0.0") version = new Version(file.Attributes["version"].Value);
                            else if (new Version(file.Attributes["version"].Value) > version) version = new Version(file.Attributes["version"].Value);

                            updatedfiles.Add(file.Attributes["path"].Value, new KeyValuePair<string,string>(file.Attributes["checksum"].Value, file.Attributes["version"].Value));
                            break;
                        }
                        else
                        {
                            break;
                        }
                    }
                }

                if (!found)
                {
                    if (version.ToString() == "0.0") version = new Version(file.Attributes["version"].Value);
                    else if (new Version(file.Attributes["version"].Value) > version) version = new Version(file.Attributes["version"].Value);

                    updatedfiles.Add(file.Attributes["path"].Value, new KeyValuePair<string, string>(file.Attributes["checksum"].Value, file.Attributes["version"].Value));
                    xmlDoc.SelectSingleNode("/manifest").AppendChild(xmlDoc.ImportNode(file.Clone(), true));
                    xmlDoc.Save(Application.StartupPath + "/manifest.xml");
                }
            }
        }

        private void DownloadUpdate()
        {
            WebClient wc = new WebClient();
            wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
            wc.DownloadFileAsync(new Uri(@"http://dl.dropbox.com/u/2683550/Heroes%20of%20Newerth/NewerthBot.zip"), Application.StartupPath + "/NewerthBot.zip");

            while (wc.IsBusy) { };
        }

        private void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            this.Invoke(new EventHandler(
                delegate
                {
                    int KBReceived = (int)e.BytesReceived / 1024;
                    int KBToReceive = (int)e.TotalBytesToReceive / 1024;
                    lblProgress.Text = "Downloaded " + KBReceived + " KB of " + KBToReceive + " KB";
                    lblPercent.Text = e.ProgressPercentage + "%";
                    progressBar.Value = e.ProgressPercentage;
                }));
        }

        private void ExtractUpdate()
        {
            using (ZipFile zip = ZipFile.Read(Application.StartupPath + "/NewerthBot.zip"))
            {
                foreach (ZipEntry entry in zip)
                {
                    if (updatedfiles.ContainsKey(entry.FileName) || !File.Exists(Application.StartupPath + "/" + entry.FileName))
                    {
                        entry.Extract(ExtractExistingFileAction.OverwriteSilently);
                    }
                }
            }

            File.Delete(Application.StartupPath + "/NewerthBot.zip");
        }

        private void UpdateManifest()
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlNodeList manifest;
            xmlDoc.Load(Application.StartupPath + "/manifest.xml");
            manifest = xmlDoc.SelectNodes("/manifest/file");

            foreach (XmlNode file in manifest)
            {
                if (updatedfiles.ContainsKey(file.Attributes["path"].Value))
                {
                    file.Attributes["checksum"].Value = updatedfiles[file.Attributes["path"].Value].Key;
                    file.Attributes["version"].Value = updatedfiles[file.Attributes["path"].Value].Value;
                }
            }

            xmlDoc.ChildNodes[1].Attributes["version"].Value = version.ToString();
            xmlDoc.Save(Application.StartupPath + "/manifest.xml");
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (inProgress)
            {
                if (MessageBox.Show(this, "Update is in progress, are you sure you wish to quit?", "NewerthUpdater", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    if (File.Exists(Application.StartupPath + "/NewerthBot.zip"))
                    {
                        try
                        {
                            File.Delete(Application.StartupPath + "/NewerthBot.zip");
                        }
                        catch (Exception) { }
                    }

                    Application.Exit();
                }
            }
            else
            {
                Application.Exit();
            }
        }
    }
}
